package com.zhuangjie.mvc.enhance.mp_query_enhance;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.core.conditions.AbstractLambdaWrapper;
import com.baomidou.mybatisplus.core.conditions.SharedString;
import com.baomidou.mybatisplus.core.conditions.query.Query;
import com.baomidou.mybatisplus.core.conditions.segments.MergeSegments;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.zhaungjie.base.utils.LambdaStructureParser.LambdaStructureParser;
import com.zhaungjie.base.utils.ReflectionUtils;
import com.zhaungjie.base.utils.StringUtils;
import com.zhuangjie.mvc.enhance.mp_query_enhance.entity.AggregatorMeta;
import com.zhuangjie.mvc.enhance.mp_query_enhance.enums.AggregationOperation;
import lombok.Getter;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

class QueryOperation<T, R> {
    private LambdaQueryWrapperPlus<T, R> wrapper;
    private Class<T> entityClass;

    public QueryOperation(Class<T> entityClass, LambdaQueryWrapperPlus<T, R> coreQueryWrapper) {
        this.entityClass = entityClass;
        this.wrapper = coreQueryWrapper;
    }


    private static String as(String column, String attr) {
        return column + " as " + attr;
    }

    /**
     * 获取重合的查询columns
     *
     * @param entityClass
     * @param targetClass
     * @return
     */
    private String[] getSelectSqlOnlyTargetOverlappingFields(Class<T> entityClass, Class<?> targetClass) {
        Map<Field, String> entityFieldMap = ReflectionUtils.getDeclaredFields(entityClass, true);
        // 过滤掉静态或常量属性
        entityFieldMap.keySet().removeIf(field -> Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers()));
        Map<Field, String> targetFieldMap = ReflectionUtils.getDeclaredFields(targetClass, true);
        Map<Field, String> targetColumns = new HashMap<>();
        for (Map.Entry<Field, String> entry : entityFieldMap.entrySet()) {
            String value = entry.getValue();
            if (targetFieldMap.containsValue(value)) {
                targetColumns.put(entry.getKey(), value);
            }
        }
        return getSelectColumns(targetColumns);
    }

    /**
     * 获取只查询的Entity类的Field列表
     *
     * @param fields
     * @return
     */
    public static String[] getSelectColumns(List<Field> fields) {
        return getSelectColumns(fields.stream().collect(Collectors.toMap(field -> field, field -> field.getName())));
    }

    /**
     * 获取只查询的Entity类的Field列表，通过传入targetColumns
     * 是getSelectColumns的更内层方法
     *
     * @param targetColumns
     * @return
     */
    private static String[] getSelectColumns(Map<Field, String> targetColumns) {
        if (targetColumns.isEmpty()) {
            // 如果都没有匹配上，没有重合的
            throw new RuntimeException("没有重合的查询columns");
        }
        targetColumns.entrySet().forEach(entry -> {
            if (tryAs(entry, TableField.class, TableField::value) && tryAs(entry, TableId.class, TableId::value)) ;
        });
        return targetColumns.values().toArray(new String[targetColumns.values().size()]);
    }


    /**
     * 传入属性名获取entityClass属性上标注的列名
     *
     * @param name
     * @return
     */
    public static String getSelectColumn(Class<?> clazz, String name) {
        try {
            Field field = ReflectionUtils.getField(clazz, name);
            TableField tableField = field.getAnnotation(TableField.class);
            if (tableField != null) return tableField.value();
            TableId tableId = field.getAnnotation(TableId.class);
            if (tableId != null) return tableId.value();
        } catch (NoSuchFieldException e) {
        }
        throw new RuntimeException("无法获取指定fieldName的Field对象！，请检查是否存在.");
    }


    /**
     * 驼峰转下划线
     *
     * @param param
     * @return
     */
    public static String camelToUnderline(String param) {
        if (param == null || "".equals(param.trim())) {
            return "";
        }
        int len = param.length();
        StringBuilder sb = new StringBuilder(len);
        for (int i = 0; i < len; i++) {
            char c = param.charAt(i);
            if (Character.isUpperCase(c)) {
                sb.append('_');
                sb.append(Character.toLowerCase(c));
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }

    /**
     * 尝试修改查询字段（as）
     *
     * @param entry
     * @param targetAnnotation
     * @param fun
     * @param <T>
     * @return
     */
    public static <T extends Annotation> boolean tryAs(Map.Entry<Field, String> entry, Class<T> targetAnnotation, Function<T, String> fun) {
        Field field = entry.getKey();
        T tableFieldAnnotation = ReflectionUtils.getAnnotation(field, targetAnnotation);
        if (tableFieldAnnotation != null) {
            String columnName = fun.apply(tableFieldAnnotation);
            String fieldName = entry.getValue();
            String underlineFieldName = camelToUnderline(fieldName);
            if (columnName == null || Objects.equals(columnName, underlineFieldName)) {
                entry.setValue(underlineFieldName);
            } else {
                entry.setValue(as(columnName, underlineFieldName));

            }
            return columnName != null;
        }
        return false;
    }

    public LambdaQueryWrapperPlus<T, R> selectColumnsBy(Class<?> targetClass) {
        Object targetObj;
        try {
            targetObj = targetClass.newInstance();
        } catch (Exception e) {
            throw new RuntimeException("无法创建select(clazz) 中clazz对象！" + e.getMessage());
        }
        String[] columns = getSelectSqlOnlyTargetOverlappingFields(entityClass, targetObj.getClass());
        return wrapper.select(columns);
    }

}

/**
 * LambdaQueryWrapper Plus
 * 特点：重合列查询
 *
 * @author zhuangjie
 * @date 2024/04/25
 */
public class LambdaQueryWrapperPlus<T, R> extends AbstractLambdaWrapper<T, LambdaQueryWrapperPlus<T, R>>
        implements Query<LambdaQueryWrapperPlus<T, R>, T, SFunction<T, ?>> {
    private Class<R> rClass;
    @Getter
    private Map<String, String> groupByKeyMap;


    public Class<R> getRClass() {
        return rClass;
    }

    /**
     * 查询字段
     */
    private SharedString sqlSelect = new SharedString();

    public LambdaQueryWrapperPlus(T entity) {
        super.setEntity(entity);
        super.initNeed();


    }

    public LambdaQueryWrapperPlus(Class<T> entityClass, Class<R> rClass) {
        this(entityClass);
        this.rClass = rClass;
        this.select(rClass);
    }

    public LambdaQueryWrapperPlus(Class<T> entityClass) {
        super.setEntityClass(entityClass);
        super.initNeed();
    }

    LambdaQueryWrapperPlus(T entity, Class<T> entityClass, SharedString sqlSelect, AtomicInteger paramNameSeq,
                           Map<String, Object> paramNameValuePairs, MergeSegments mergeSegments, SharedString paramAlias,
                           SharedString lastSql, SharedString sqlComment, SharedString sqlFirst) {
        super.setEntity(entity);
        super.setEntityClass(entityClass);
        this.paramNameSeq = paramNameSeq;
        this.paramNameValuePairs = paramNameValuePairs;
        this.expression = mergeSegments;
        this.sqlSelect = sqlSelect;
        this.paramAlias = paramAlias;
        this.lastSql = lastSql;
        this.sqlComment = sqlComment;
        this.sqlFirst = sqlFirst;
    }


    public LambdaQueryWrapperPlus<T, R> select(String... columns) {
        if (columns.length > 0) {
            SharedString sharedString = ReflectionUtils.getFieldValue(this, SharedString.class);
            sharedString.setStringValue(String.join(StringPool.COMMA, columns));
        }
        return typedThis;
    }

    public LambdaQueryWrapperPlus<T, R> selectForFieldName(String... columns) {
        if (columns != null && columns.length > 0) {
            // entity的所有属性信息
            Map<Field, String> fields = ReflectionUtils.getDeclaredFields(getEntityClass(), true);
            // 创建快查Set: 获取fields属性名中与columns匹配的Field列表作为查询
            Set<String> columnSet = Arrays.stream(columns).collect(Collectors.toSet());
            // 查询的列：QueryOperation.getSelectColumns(List<Field> fields) 会看属性上的注解，结合着来生成最终的查询sql
            String[] selectColumns = QueryOperation.getSelectColumns(fields.entrySet().stream()
                    .filter(entry -> columnSet.contains(entry.getValue()))
                    .map(Map.Entry::getKey)
                    .collect(Collectors.toList()));
            select(selectColumns);
        }


        return null;
    }

    /**
     * SELECT 部分 SQL 设置
     *
     * @param columns 查询字段
     */
    @SafeVarargs
    @Override
    public final LambdaQueryWrapperPlus<T, R> select(SFunction<T, ?>... columns) {
        return select(Arrays.asList(columns));
    }

    /**
     * 返回聚合运算列的别名
     *
     * @param aggregationOperation
     * @param fieldName
     * @return
     */
    public static String getAggregationOperationAlias(AggregationOperation aggregationOperation, String fieldName) {
        // 如返回: avgName
        return aggregationOperation.name().toLowerCase() + (StringUtils.firstToUpperCase(fieldName));
    }

    @Override
    public LambdaQueryWrapperPlus<T, R> groupBy(SFunction<T, ?> column) {
        super.groupBy(column);
        if (this.groupByKeyMap == null) {
            this.groupByKeyMap = new HashMap<>();
        }
        // 添加groupBy key item
        String fieldName = LambdaStructureParser.convertToFieldNameHandler(column);
        this.groupByKeyMap.put(QueryOperation.getSelectColumn(getEntityClass(), fieldName), fieldName);
        return this;
    }

    public final LambdaQueryWrapperPlus<T, R> selectForUseGroupBy(List<AggregatorMeta> aggregators) {
        if (aggregators != null && !aggregators.isEmpty()) {
            List<String> selectSql = aggregators.stream().map(aggregatorMeta -> {
                Class<T> entityClass = getEntityClass();
                String fieldName = aggregatorMeta.getFiledName();
                AggregationOperation aggregationOperation = aggregatorMeta.getAggregationOperation();
                String selectColumn = QueryOperation.getSelectColumn(entityClass, fieldName);
                String aggregationOperationName = aggregationOperation.name().toLowerCase();
                return aggregationOperationName + "(" + selectColumn + ") as " + getAggregationOperationAlias(aggregationOperation, fieldName);
            }).collect(Collectors.toList());
            // 不仅聚合还要分组的keys也要查
            List<String> aggSelectColumns = groupByKeyMap.entrySet().stream().map(entry -> entry.getKey() + " as " + entry.getValue()).collect(Collectors.toList());
            selectSql.addAll(aggSelectColumns);
            if (!selectSql.isEmpty()) {
                select(selectSql.toArray(new String[selectSql.size()]));
            }
        }
        return this;
    }

    public LambdaQueryWrapperPlus<T, R> select(List<SFunction<T, ?>> columns) {
        if (CollectionUtils.isNotEmpty(columns)) {
            this.sqlSelect.setStringValue(columnsToString(false, columns));
        }
        return typedThis;
    }

    /**
     * 过滤查询的字段信息(主键除外!)
     * <p>例1: 只要 java 字段名以 "test" 开头的             -> select(i -&gt; i.getProperty().startsWith("test"))</p>
     * <p>例2: 只要 java 字段属性是 CharSequence 类型的     -> select(TableFieldInfo::isCharSequence)</p>
     * <p>例3: 只要 java 字段没有填充策略的                 -> select(i -&gt; i.getFieldFill() == FieldFill.DEFAULT)</p>
     * <p>例4: 要全部字段                                   -> select(i -&gt; true)</p>
     * <p>例5: 只要主键字段                                 -> select(i -&gt; false)</p>
     *
     * @param predicate 过滤方式
     * @return this
     */
    @Override
    public LambdaQueryWrapperPlus<T, R> select(Class<T> entityClass, Predicate<TableFieldInfo> predicate) {
        if (entityClass == null) {
            entityClass = getEntityClass();
        } else {
            setEntityClass(entityClass);
        }
        Assert.notNull(entityClass, "entityClass can not be null");
        this.sqlSelect.setStringValue(TableInfoHelper.getTableInfo(entityClass).chooseSelect(predicate));
        return typedThis;
    }

    @Override
    public String getSqlSelect() {
        return sqlSelect.getStringValue();
    }

    /**
     * 用于生成嵌套 sql
     * <p>故 sqlSelect 不向下传递</p>
     */
    @Override
    protected LambdaQueryWrapperPlus<T, R> instance() {
        return new LambdaQueryWrapperPlus<>(getEntity(), getEntityClass(), null, paramNameSeq, paramNameValuePairs,
                new MergeSegments(), paramAlias, SharedString.emptyString(), SharedString.emptyString(), SharedString.emptyString());
    }

    @Override
    public void clear() {
        super.clear();
        sqlSelect.toNull();
    }


    public LambdaQueryWrapperPlus<T, R> select(Class<R> rClass) {
        Class<T> entityClass = getEntityClass();
        QueryOperation<T, R> queryOperation = new QueryOperation<>(entityClass, this);
        queryOperation.selectColumnsBy(rClass);
        return this;
    }

    public void limit(Integer offset, Integer count) {
        if (offset == null) {
            throw new IllegalArgumentException("offset不能为null");
        }
        String lastSql1 = lastSql.getStringValue();
        String lastSql2 = count != null ? ("limit " + offset + "," + count) : ("limit " + offset);
        lastSql.setStringValue(lastSql1.concat(" ").concat(lastSql2));
    }

    public void limit(Integer count) {
        limit(0, count);
    }
}
/**
 * wrappers查询Plus
 * MybatisPlus Wrappers增强器
 *
 * @author zhuangjie
 * @date 2024/04/23
 */
//public class LambdaQueryWrapperBuilder {
//
//
//
//
//
//    public static<T> QueryOperation<T> wrapperBuilder(Class<T> clazz, Consumer<LambdaQueryWrapper<T>> wrapperConsumer) {
//        if (clazz == null) {
//            throw new IllegalArgumentException("需要传入LambdaQueryWrapper，但出现了null值！");
//        }
//        LambdaQueryWrapperPlus<T> coreQueryWrapper = new LambdaQueryWrapperPlus<>(clazz);
//        wrapperConsumer.accept(coreQueryWrapper);
//        return new QueryOperation<T>(clazz,coreQueryWrapper);
//    }
//
//    public static<T> QueryOperation<T> wrapperBuilder(Class<T> clazz, LambdaQueryWrapper<T> inWrapper) {
//        if (clazz == null) {
//            throw new IllegalArgumentException("需要传入LambdaQueryWrapper，但出现了null值！");
//        }
//        return new QueryOperation<T>(clazz,inWrapper);
//    }
//
//
//}
